﻿using System.Linq.Expressions;
using System.Reflection;
using System.Security.Claims;
using System.Web;
using ApiLib.AppStart;
using ApiLib.Common;
using ApiLib.Common.Attributes;
using ApiLib.Helpers;
using ApiLib.Models;
using ApiLib.Utils;
using CSRedis;
using Furion;
using Furion.DataValidation;
using Furion.FriendlyException;
using Furion.JsonSerialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using SqlSugar;

namespace ApiLib.Controllers;

/// <summary>
/// 控制器基类
/// </summary>
public class BaseController : ControllerBase
{
    /// <summary>
    /// 数据库
    /// </summary>
    protected readonly ISqlSugarClient Db;

    /// <summary>
    /// 数据库
    /// </summary>
    protected readonly SqlSugarScope? DbScope;

    /// <summary>
    /// 序列化
    /// </summary>
    protected readonly IJsonSerializerProvider JsonSerializer;

    /// <summary>
    /// 缓存
    /// </summary>
    protected readonly IMemoryCache MemoryCache;

    /// <summary>
    /// Redis
    /// </summary>
    protected CSRedisClient? Redis => DbHelper.Redis;

    /// <summary>
    /// 用户Sid
    /// </summary>
    public readonly string Sid;

    /// <summary>
    /// token
    /// </summary>
    protected string Token;

    /// <summary>
    /// 用户信息
    /// </summary>
    protected IBaseUser? BaseUser;

    /// <summary>
    /// 文件上传文件夹
    /// </summary>
    protected static string UploadDictionary => GlobalConfig.UploadPath;

    /// <summary>
    /// 系统数据关键字
    /// </summary>
    protected static string SystemKey => GlobalConstants.SystemConfigKey.SystemKey;

    /// <summary>
    /// 用户认证
    /// </summary>
    protected static IBaseAuthorization? BaseAuthorization => GlobalConfig.BaseAuthorization;

    /// <summary>
    /// 构造方法
    /// </summary>
    public BaseController()
    {
        Db = App.GetService<ISqlSugarClient>();
        if (Db is SqlSugarScope scope)
        {
            DbScope = scope;
        }

        MemoryCache = App.GetService<IMemoryCache>();
        JsonSerializer = JSON.GetJsonSerializer();

        Sid = App.User?.FindFirstValue(ClaimTypes.Sid) ?? "";
        Token = BaseAuthorization?.GetToken() ?? "";
        if (!string.IsNullOrEmpty(Token))
        {
            BaseUser = BaseAuthorization?.Authorization(Request);
        }
    }

    #region 文件相关

    /// <summary>
    /// 保存文件到服务器
    /// </summary>
    /// <param name="file"></param>
    /// <param name="fileName"></param>
    /// <returns></returns>
    protected string SaveFile(IFormFile file, string fileName = "")
    {
        var month = DateTime.Now.ToString("yyyyMM");
        var savePath = Path.Combine(AppContext.BaseDirectory, UploadDictionary, month);
        if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath);
        var ext = Path.GetExtension(file.FileName); //后缀名

        // 避免文件名重复，采用 GUID 生成
        var guid = Guid.NewGuid().ToString("N");
        if (string.IsNullOrEmpty(fileName))
        {
            fileName = guid + ext;
        }

        var filePath = Path.Combine(savePath, fileName);
        // 保存到指定路径
        using var stream = System.IO.File.Create(filePath);
        file.CopyTo(stream);
        //保存数据库
        var relatePath = Path.Combine(UploadDictionary, month, fileName);
        if (GlobalConfig.UseDb)
        {
            Db.Insertable(new SysFileInfo
            {
                CreateUser = Sid,
                Owner = Sid,
                CreateTime = LocalTime,
                Length = file.Length,
                Key = guid,
                Version = "1",
                Name = HttpUtility.UrlDecode(file.FileName),
                Path = relatePath,
                Ext = ext
            }).ExecuteCommand();
        }

        return relatePath;
    }

    /// <summary>
    /// Image存储到本地
    /// </summary>
    /// <param name="bytes"></param>
    /// <param name="fileName">文件名</param>
    /// <returns></returns>
    protected string SaveFile(byte[]? bytes, string fileName = "")
    {
        if (bytes == null) return "";

        var month = DateTime.Now.ToString("yyyyMM");
        var savePath = Path.Combine(AppContext.BaseDirectory, GlobalConfig.UploadPath, month);
        if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath);


        // 避免文件名重复，采用 GUID 生成
        var guid = Guid.NewGuid().ToString("N");
        if (string.IsNullOrEmpty(fileName))
        {
            fileName = guid + ".png";
        }

        var filePath = Path.Combine(savePath, fileName);
        System.IO.File.WriteAllBytes(filePath, bytes);
        //保存数据库
        var relatePath = Path.Combine(UploadDictionary, month, fileName);
        if (GlobalConfig.UseDb)
        {
            Db.Insertable(new SysFileInfo
            {
                CreateUser = Sid,
                Owner = Sid,
                CreateTime = LocalTime,
                Length = bytes.Length,
                Key = guid,
                Version = "1",
                Name = fileName,
                Path = relatePath,
                Ext = Path.GetExtension(fileName)
            }).ExecuteCommand();
        }

        return relatePath;
    }

    /// <summary>
    /// 获取服务器文件
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <returns>流</returns>
    protected Stream GetFile(string fileName)
    {
        if (fileName.StartsWith("/"))
        {
            fileName = fileName.Substring(1, fileName.Length - 1);
        }

        var filePath = Path.Combine(AppContext.BaseDirectory, fileName);
        return new FileStream(filePath, FileMode.Open);
    }

    /// <summary>
    /// 从文件中获取数据.Excel特性
    /// </summary>
    /// <param name="file">表单文件</param>
    /// <typeparam name="T">数据类型</typeparam>
    /// <returns>数据列表</returns>
    protected List<T> GetExcelData<T>(IFormFile file) where T : class
    {
        return file.GetWorkbook().GetExcelData<T>();
    }

    /// <summary>
    /// 数据导出到Excel文件.Excel特性
    /// </summary>
    /// <param name="infos">数据</param>
    /// <typeparam name="T">数据类型</typeparam>
    /// <returns>数据列表</returns>
    protected MemoryStream WriteExcelData<T>(IEnumerable<T> infos) where T : class, new()
    {
        return infos.GetExcelWorkbook().Write();
    }

    /// <summary>
    /// 获取文件结果，用于返回文件
    /// </summary>
    /// <param name="stream">流</param>
    /// <param name="fileName">文件名</param>
    /// <param name="contentType">文件类型</param>
    /// <returns></returns>
    protected FileResult GetFileResult(Stream stream, string fileName,
        string contentType = GlobalConstants.FileContentType.Normal)
    {
        if (stream is MemoryStream ms)
        {
            return File(ms.ToArray(), contentType, fileName);
        }

        return File(stream, contentType, fileName);
    }

    /// <summary>
    /// 获取文件信息
    /// </summary>
    /// <param name="filepath">文件地址</param>
    /// <returns></returns>
    protected SysFileInfo? GetFileInfo(string filepath)
    {
        var sysUploadFile = Db.Queryable<SysFileInfo>().First(file => file.Path.Contains(filepath));
        return sysUploadFile;
    }

    /// <summary>
    /// 获取文件信息
    /// </summary>
    /// <param name="id">文件id</param>
    /// <returns></returns>
    protected SysFileInfo? GetFileInfo(int id)
    {
        var sysUploadFile = Db.Queryable<SysFileInfo>().InSingle(id);
        return sysUploadFile;
    }

    #endregion

    #region 配置文件

    /// <summary>
    /// 获取系统配置
    /// </summary>
    /// <param name="key">键</param>
    /// <returns></returns>
    protected SysConfig? GetSystemConfig(string key)
    {
        return GetConfig(SystemKey, key);
    }

    /// <summary>
    /// 保存系统配置
    /// </summary>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <returns></returns>
    protected void SetSystemConfig(string key, string value)
    {
        SetConfig(SystemKey, key, value);
    }

    /// <summary>
    /// 获取配置
    /// </summary>
    /// <param name="name">名称</param>
    /// <param name="key">键</param>
    /// <returns></returns>
    protected SysConfig? GetConfig(string name, string key)
    {
        return Db.Queryable<SysConfig>()
            .First(config => config.Name == name && config.Key == key);
    }

    /// <summary>
    /// 保存配置
    /// </summary>
    /// <param name="name">名称</param>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <returns></returns>
    protected void SetConfig(string name, string key, string value)
    {
        var first = Db.Queryable<SysConfig>()
            .First(config => config.Name == name && config.Key == key);
        if (first == null)
        {
            first = new SysConfig
            {
                Name = name,
                Key = key,
                Value = value,
                CreateUser = Sid,
                CreateTime = LocalTime,
            };
            Db.Insertable(first).ExecuteCommand();
        }
        else
        {
            first.Value = value;
            first.UpdateUser = Sid;
            first.UpdateTime = LocalTime;
            Db.Updateable(first).ExecuteCommand();
        }
    }

    #endregion

    #region 用户处理

    /// <summary>
    /// 获取用户
    /// </summary>
    /// <param name="sid"></param>
    /// <returns></returns>
    protected IBaseUser? GetUser(string sid)
    {
        return BaseAuthorization?.GetUser(sid);
    }

    /// <summary>
    /// 当前用户
    /// </summary>
    protected IBaseUser CurrentUser => GetCurrentUser();

    /// <summary>
    /// 获取当前用户
    /// </summary>
    /// <returns>当前用户</returns>
    protected IBaseUser GetCurrentUser()
    {
        if (BaseUser == null)
        {
            throw ActionException("用户登录异常");
        }

        return BaseUser;
    }

    /// <summary>
    /// 获取用户名
    /// </summary>
    /// <param name="sid"></param>
    /// <returns></returns>
    protected string GetUserName(string sid)
    {
        var user = GetUser(sid);
        return user?.GetName() ?? "";
    }

    /// <summary>
    /// 初始化实体对象，
    /// </summary>
    /// <param name="obj"></param>
    protected bool InitAddAndUpdate(object obj)
    {
        return obj.InitAddAndUpdate(BaseUser?.GetSid());
    }

    #endregion

    #region 日志相关

    /// <summary>
    /// 创建系统日志，存储数据库。如果使用mongo，不建议再存储到数据库。
    /// </summary>
    /// <param name="action">行为</param>
    /// <param name="content">内容</param>
    /// <param name="remark">备注</param>
    protected void LogToDb(string action, string content, string remark)
    {
        var sysLog = new SysLog
        {
            DataType = SystemKey,
            Action = action,
            Content = content,
            Remark = remark,
            CreateUser = Sid,
            CreateTime = LocalTime
        };
        Db.Insertable(sysLog).ExecuteCommand();
        LogInfo(sysLog.Serialize());
    }

    /// <summary>
    /// 普通日志
    /// </summary>
    /// <param name="msg"></param>
    protected void LogInfo(string msg)
    {
        WebUtil.LogInfo(msg);
    }

    /// <summary>
    /// 错误日志
    /// </summary>
    /// <param name="msg"></param>
    protected void LogErr(string msg)
    {
        WebUtil.LogExp(msg);
    }

    /// <summary>
    /// 错误日志
    /// </summary>
    /// <param name="msg"></param>
    /// <param name="e"></param>
    protected void LogErr(string msg, Exception e)
    {
        WebUtil.Error(msg, e);
    }

    #endregion

    #region 异常

    /// <summary>
    /// 抛出接口异常
    /// </summary>
    /// <param name="msg">异常信息</param>
    /// <exception cref="Exception"></exception>
    protected Exception ActionException(string msg)
    {
        return new MyActionException(msg);
    }

    /// <summary>
    /// 抛出服务异常
    /// </summary>
    /// <param name="msg">异常信息</param>
    /// <exception cref="Exception"></exception>
    protected Exception ServiceException(string msg)
    {
        return new MyServiceException(msg);
    }

    /// <summary>
    /// 抛出授权异常
    /// </summary>
    /// <param name="msg">异常信息</param>
    /// <exception cref="Exception"></exception>
    protected Exception UnauthorizedAccessException(string msg)
    {
        return new MyUnauthorizedAccessException(msg);
    }

    /// <summary>
    /// 抛出认证异常
    /// </summary>
    /// <param name="msg">异常信息</param>
    /// <exception cref="Exception"></exception>
    protected Exception AuthenticationFailureException(string msg)
    {
        return new MyAuthenticationFailureException(msg);
    }

    /// <summary>
    /// 验证异常，不打印日志。（属于验证异常的一种）
    /// </summary>
    /// <param name="msg">异常信息</param>
    /// <exception cref="Exception"></exception>
    protected Exception ValidationException(string msg)
    {
        LogErr(msg);
        return new MyValidationException(msg);
    }

    #endregion

    /// <summary>
    /// 当前时间
    /// </summary>
    protected static DateTime LocalTime => CommonUtil.LocalDateTime();

    /// <summary>
    /// 返回404
    /// </summary>
    /// <returns></returns>
    protected IActionResult Status404()
    {
        return BadPageResult.Status404NotFound;
        // return new BadPageResult(404);
    }

    /// <summary>
    /// 检查权限，没有权限会抛出异常
    /// </summary>
    /// <param name="perm"></param>
    /// <exception cref="Exception"></exception>
    protected virtual void CheckPerm(string perm)
    {
        //系统不需要登陆
        if (BaseAuthorization == null) return;

        //未登录异常
        if (BaseUser == null) throw AuthenticationFailureException("未登录");

        //不需要权限
        if (BaseUser.IsSuperAdmin() || string.IsNullOrEmpty(perm)) return;

        //检查权限
        var permissions = BaseUser.GetPermissions();
        if (permissions == null || !permissions.Contains(perm)) throw UnauthorizedAccessException("没有权限");
    }

    /// <summary>
    /// 检查权限
    /// </summary>
    /// <param name="perm"></param>
    /// <exception cref="Exception"></exception>
    protected virtual bool HasPerm(string perm)
    {
        //未登录异常
        if (BaseUser == null) throw AuthenticationFailureException("未登录");

        //不需要权限
        if (BaseUser.IsSuperAdmin() || string.IsNullOrEmpty(perm)) return true;

        //检查权限
        var permissions = BaseUser.GetPermissions();
        return permissions != null && permissions.Contains(perm);
    }
}

/// <summary>
/// 仓储功能
/// </summary>
/// <typeparam name="T"></typeparam>
public class BaseController<T> : BaseController where T : class, new()
{
    /// <summary>
    /// 
    /// </summary>
    protected readonly Repository<T> Repo;

    /// <summary>
    /// 
    /// </summary>
    protected Repository<T> Dal => Repo;

    /// <summary>
    /// 
    /// </summary>
    public BaseController()
    {
        Repo = new Repository<T>();
    }
}

/// <summary>
/// 高度定制化：如果不需要某个接口，可覆写后实现[NonAction]特性
/// </summary>
/// <typeparam name="T">数据库对象</typeparam>
/// <typeparam name="TP">查询参数对象</typeparam>
public abstract class BaseController<T, TP> : BaseController<T>
    where T : BaseEntityIdentity, new()
    where TP : PageParam
{
    /// <summary>
    /// 获取权限
    /// </summary>
    /// <returns></returns>
    protected virtual BaseSsInfo GetSsInfo()
    {
        return new BaseSsInfo() { FileName = DateTime.Now.TimeSeconds() + ".xlsx" };
    }

    /// <summary>
    /// 权限对象
    /// </summary>
    protected virtual BaseSsInfo SsInfo => GetSsInfo();

    /// <summary>
    /// 查询
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    protected virtual ISugarQueryable<T> GetQueryable(TP param)
    {
        return Repo.AsQueryable();
    }

    /// <summary>
    /// 列表
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public virtual ListResponse<T> List(TP param)
    {
        CheckPerm(SsInfo.SsList);
        return GetQueryable(param).ToPageList(param);
    }

    /// <summary>
    /// 详情
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual T Detail(int id)
    {
        CheckPerm(SsInfo.SsList);
        return Repo.GetById(id);
    }

    /// <summary>
    /// 保存前检查对象，完成一些附加操作，比如：添加用户等
    /// </summary>
    /// <param name="info"></param>
    protected virtual void SaveCheckEntity(T info)
    {
    }

    /// <summary>
    /// 保存
    /// </summary>
    /// <param name="info"></param>
    /// <returns></returns>
    public virtual int Save(T info)
    {
        CheckPerm(info.Id == 0 ? SsInfo.SsAdd : SsInfo.SsSave);
        info.InitSaveUser();
        SaveCheckEntity(info);
        if (info.Id == 0)
        {
            info.Id = Repo.InsertReturnIdentity(info);
            if (info.Id == 0)
            {
                throw ActionException("新增失败");
            }
        }
        else
        {
            var update = Repo.Update(info);
            if (!update)
            {
                throw ActionException("更新失败");
            }
        }

        return info.Id;
    }

    /// <summary>
    /// 删除
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public virtual bool Delete(int id)
    {
        CheckPerm(SsInfo.SsDel);
        return Repo.DeleteById(id);
    }

    /// <summary>
    /// 导出
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public virtual FileResult Export(TP param)
    {
        CheckPerm(SsInfo.SsExport);
        var infos = GetQueryable(param).ToList();
        var memoryStream = WriteExcelData(infos);
        return GetFileResult(memoryStream, SsInfo.FileName);
    }

    /// <summary>
    /// 导出模版
    /// </summary>
    /// <returns></returns>
    public virtual FileResult ExportTemplate()
    {
        var infos = new List<T>()
        {
            new()
        };
        var memoryStream = WriteExcelData(infos);
        return GetFileResult(memoryStream, SsInfo.FileName);
    }

    /// <summary>
    /// 导出模版（不到处基础类字段：创建人、创建时间等）
    /// </summary>
    /// <param name="instance">实体对象</param>
    /// <param name="withoutBaseEntity">排除基础类的属性</param>
    /// <param name="func">委托：返回为空字符串时，不导出</param>
    /// <returns></returns>
    protected FileResult ExportTemplateBase(T instance, bool withoutBaseEntity = true,
        Func<PropertyInfo, string>? func = null)
    {
        var type = typeof(T);
        var properties = type.GetProperties();
        var obj = new Dictionary<string, object>();

        foreach (var property in properties)
        {
            var excel = property.GetCustomAttribute(typeof(ExcelAttribute)) as ExcelAttribute;
            excel ??= new ExcelAttribute(property.Name);
            if (excel is { IsIgnore: true })
            {
                continue;
            }

            var columnName = excel?.Name ?? property.Name;

            if (withoutBaseEntity && property.DeclaringType == typeof(BaseEntity))
            {
                continue;
            }

            if (func != null)
            {
                columnName = func.Invoke(property);
            }

            if (string.IsNullOrEmpty(columnName))
            {
                continue;
            }

            obj.Add(columnName, property.GetFieldValue(instance) ?? "");
        }

        var list = new List<Dictionary<string, object>>() { obj };
        var workbook = list.ToWorkbook();
        var stream = workbook.Write();
        return GetFileResult(stream, SsInfo.FileName);
    }

    /// <summary>
    /// 导入前检查对象，完成一些附加操作，比如：添加用户等
    /// </summary>
    /// <param name="infos"></param>
    protected virtual void ImportCheckEntity(List<T> infos)
    {
    }

    /// <summary>
    /// 导入
    /// </summary>
    /// <param name="file"></param>
    /// <returns></returns>
    public virtual bool Import(IFormFile file)
    {
        CheckPerm(SsInfo.SsImport);
        var infos = GetExcelData<T>(file);

        foreach (var info in infos)
        {
            info.InitSaveUser();

            var validate = info.TryValidate();
            if (!validate.IsValid)
            {
                var enumerable = validate.ValidationResults.Select(r => r.ErrorMessage);
                throw ActionException(string.Join(",", enumerable));
            }
        }

        ImportCheckEntity(infos);
        return Repo.InsertRange(infos);
    }
}

/// <summary>
/// 仓储功能
/// </summary>
/// <typeparam name="T"></typeparam>
public class Repository<T> : SimpleClient<T> where T : class, new()
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    public Repository(ISqlSugarClient context = null) : base(context) //默认值等于null不能少
    {
        Context = App.GetService<ISqlSugarClient>(); //用手动获取方式支持切换仓储
    }

    /// <summary>
    /// 分页查询
    /// </summary>
    /// <param name="param">页码</param>
    /// <param name="whereExpression">where条件</param>
    /// <param name="orderByExpression">排序条件</param>
    /// <param name="orderByType">排序</param>
    /// <returns></returns>
    public ListResponse<T> GetPageList(PageParam param, Expression<Func<T, bool>>? whereExpression = null,
        Expression<Func<T, object>>? orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
    {
        // Expression<Func<T, bool>> exp = arg => true;
        var whereExp = whereExpression ?? (arg => true);
        var pageModel = new PageModel { PageIndex = param.PageIndex, PageSize = param.PageSize };
        var pageList = base.GetPageList(whereExp, pageModel, orderByExpression, orderByType);
        return new ListResponse<T> { List = pageList, TotalCount = pageModel.TotalCount };
    }
}

/// <summary>
/// 基础权限要求
/// </summary>
public class BaseSsInfo
{
    /// <summary>
    /// 导出文件名
    /// </summary>
    public string FileName { get; set; } = "";

    /// <summary>
    /// 列表权限
    /// </summary>
    public string SsList { get; set; } = "";

    /// <summary>
    /// 保存权限
    /// </summary>
    public string SsAdd { get; set; } = "";

    /// <summary>
    /// 保存权限
    /// </summary>
    public string SsSave { get; set; } = "";

    /// <summary>
    /// 删除权限
    /// </summary>
    public string SsDel { get; set; } = "";

    /// <summary>
    /// 导出权限
    /// </summary>
    public string SsExport { get; set; } = "";

    /// <summary>
    /// 导入权限
    /// </summary>
    public string SsImport { get; set; } = "";
}